#define NOMINMAX
#include "OgreGLESRenderSystem.h"
#include "OgreGLESTextureManager.h"
#include "OgreGLESDefaultHardwareBufferManager.h"
#include "OgreGLESHardwareIndexBuffer.h"
#include "OgreGLESHardwareVertexBuffer.h"
#include "OgreGLESGpuProgramManager.h"
#include "OgreGLESUtil.h"
#include "OgreGLESPBRenderTexture.h"
#include "OgreGLESFBORenderTexture.h"

#if OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
#include "OgreEAGLWindow.h"
#else
#include "OgreEGLWindow.h"

#ifndef GL_GLEXT_PROTOTYPES
	PFNGLISRENDERBUFFEROESPROC glIsRenderbufferOES;
	PFNGLBINDRENDERBUFFEROESPROC glBindRenderbufferOES;
	PFNGLDELETERENDERBUFFERSOESPROC glDeleteRenderbuffersOES;
	PFNGLGENRENDERBUFFERSOESPROC glGenRenderbuffersOES;
	PFNGLRENDERBUFFERSTORAGEOESPROC glRenderbufferStorageOES;
	PFNGLGETRENDERBUFFERPARAMETERIVOESPROC glGetRenderbufferParameterivOES;
	PFNGLISFRAMEBUFFEROESPROC glIsFramebufferOES;
	PFNGLBINDFRAMEBUFFEROESPROC glBindFramebufferOES;
	PFNGLDELETEFRAMEBUFFERSOESPROC glDeleteFramebuffersOES;
	PFNGLGENFRAMEBUFFERSOESPROC glGenFramebuffersOES;
	PFNGLCHECKFRAMEBUFFERSTATUSOESPROC glCheckFramebufferStatusOES;
	PFNGLFRAMEBUFFERRENDERBUFFEROESPROC glFramebufferRenderbufferOES;
	PFNGLFRAMEBUFFERTEXTURE2DOESPROC glFramebufferTexture2DOES;
	PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC glGetFramebufferAttachmentParameterivOES;
	PFNGLGENERATEMIPMAPOESPROC glGenerateMipmapOES;
	PFNGLBLENDEQUATIONOESPROC glBlendEquationOES;
	PFNGLBLENDFUNCSEPARATEOESPROC glBlendFuncSeparateOES;
	PFNGLBLENDEQUATIONSEPARATEOESPROC glBlendEquationSeparateOES;
#endif
#endif

#include "OgreCamera.h"
#include "OgreLight.h"

#define VBO_BUFFER_OFFSET(i) ((char*)NULL + (i))

namespace Ogre
{
	GLESRenderSystem::GLESRenderSystem()
		: mDepthWrite(true),
		  mStencilMask(0xFFFFFFFF),
		  mGpuProgramManager(0),
		  mHardwareBufferManager(0),
		  mRTTManager(0),
		  mActiveTextureUnit(0)
	{
#ifndef GL_GLEXT_PROTOTYPES
		::glIsRenderbufferOES = (PFNGLISRENDERBUFFEROESPROC)eglGetProcAddress("glIsRenderbufferOES");
		::glBindRenderbufferOES = (PFNGLBINDRENDERBUFFEROESPROC)eglGetProcAddress("glBindRenderbufferOES");
		::glDeleteRenderbuffersOES = (PFNGLDELETERENDERBUFFERSOESPROC)eglGetProcAddress("glDeleteRenderbuffersOES");
		::glGenRenderbuffersOES = (PFNGLGENRENDERBUFFERSOESPROC)eglGetProcAddress("glGenRenderbuffersOES");
		::glRenderbufferStorageOES = (PFNGLRENDERBUFFERSTORAGEOESPROC)eglGetProcAddress("glRenderbufferStorageOES");
		::glGetRenderbufferParameterivOES = (PFNGLGETRENDERBUFFERPARAMETERIVOESPROC)eglGetProcAddress("glGetRenderbufferParameterivOES");
		::glIsFramebufferOES = (PFNGLISFRAMEBUFFEROESPROC)eglGetProcAddress("glIsFramebufferOES");
		::glBindFramebufferOES = (PFNGLBINDFRAMEBUFFEROESPROC)eglGetProcAddress("glBindFramebufferOES");
		::glDeleteFramebuffersOES = (PFNGLDELETEFRAMEBUFFERSOESPROC)eglGetProcAddress("glDeleteFramebuffersOES");
		::glGenFramebuffersOES = (PFNGLGENFRAMEBUFFERSOESPROC)eglGetProcAddress("glGenFramebuffersOES");
		::glCheckFramebufferStatusOES = (PFNGLCHECKFRAMEBUFFERSTATUSOESPROC)eglGetProcAddress("glCheckFramebufferStatusOES");
		::glFramebufferRenderbufferOES = (PFNGLFRAMEBUFFERRENDERBUFFEROESPROC)eglGetProcAddress("glFramebufferRenderbufferOES");
		::glFramebufferTexture2DOES = (PFNGLFRAMEBUFFERTEXTURE2DOESPROC)eglGetProcAddress("glFramebufferTexture2DOES");
		::glGetFramebufferAttachmentParameterivOES = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVOESPROC)eglGetProcAddress("glGetFramebufferAttachmentParameterivOES");
		::glGenerateMipmapOES = (PFNGLGENERATEMIPMAPOESPROC)eglGetProcAddress("glGenerateMipmapOES");
		::glBlendEquationOES = (PFNGLBLENDEQUATIONOESPROC)eglGetProcAddress("glBlendEquationOES");
		::glBlendFuncSeparateOES = (PFNGLBLENDFUNCSEPARATEOESPROC)eglGetProcAddress("glBlendFuncSeparateOES");
		::glBlendEquationSeparateOES = (PFNGLBLENDEQUATIONSEPARATEOESPROC)eglGetProcAddress("glBlendEquationSeparateOES");
#endif
		GL_CHECK_ERROR;
		size_t i;
		
		LogManager::getSingleton().logMessage(getName() + " created.");
		
		mGLSupport = getGLSupport();
		
		for (i = 0; i < MAX_LIGHTS; i++)
			mLights[i] = NULL;
		
		mWorldMatrix = Matrix4::IDENTITY;
		mViewMatrix = Matrix4::IDENTITY;
		
		mGLSupport->addConfig();
		
		mColourWrite[0] = mColourWrite[1] = mColourWrite[2] = mColourWrite[3] = true;
		
		for (i = 0; i < OGRE_MAX_TEXTURE_LAYERS; i++)
		{
			mTextureCoordIndex[i] = 99;
		}
		
		mTextureCount = 0;
		mActiveRenderTarget = 0;
		mCurrentContext = 0;
		mMainContext = 0;
		mGLInitialised = false;
		mCurrentLights = 0;
		mTextureMipmapCount = 0;
		mMinFilter = FO_LINEAR;
		mMipFilter = FO_POINT;
	}
	
	GLESRenderSystem::~GLESRenderSystem()
	{
		shutdown();
		
		RenderTargetMap::iterator i;
		for (i = mRenderTargets.begin(); i != mRenderTargets.end(); ++i)
		{
			OGRE_DELETE	i->second;
		}
		
		mRenderTargets.clear();
		OGRE_DELETE mGLSupport;
	}
	
	const String& GLESRenderSystem::getName(void) const
	{
		static String strName("OpenGL ES 1.x Rendering Subsystem");
		return strName;
	}
	
	ConfigOptionMap& GLESRenderSystem::getConfigOptions(void)
	{
		return mGLSupport->getConfigOptions();
	}
	
	void GLESRenderSystem::setConfigOption(const String& name, const String& value)
	{
		mGLSupport->setConfigOption(name, value);
	}
	
	String GLESRenderSystem::validateConfigOptions(void)
	{
		return mGLSupport->validateConfig();
	}
	
	RenderWindow* GLESRenderSystem::_initialise(bool autoCreateWindow,
												const String& windowTitle)
	{
		mGLSupport->start();
		
		RenderWindow* autoWindow = mGLSupport->createWindow(autoCreateWindow, 
															this, windowTitle);
		RenderSystem::_initialise(autoCreateWindow, windowTitle);
		return autoWindow;
	}
	
	RenderSystemCapabilities* GLESRenderSystem::createRenderSystemCapabilities() const
	{
		RenderSystemCapabilities* rsc = OGRE_NEW RenderSystemCapabilities();
		
		rsc->setCategoryRelevant(CAPS_CATEGORY_GL, true);
		rsc->setDriverVersion(mDriverVersion);
		
		const char* deviceName = (const char*)glGetString(GL_RENDERER);
		const char* vendorName = (const char*)glGetString(GL_VENDOR);
		if (deviceName)
		{
			rsc->setDeviceName(deviceName);
		}
		
		rsc->setRenderSystemName(getName());
		
		if (strstr(vendorName, "Imagination Technologies"))
			rsc->setVendor(GPU_IMAGINATION_TECHNOLOGIES);
		else if (strstr(vendorName, "Apple Computer, Inc."))
			rsc->setVendor(GPU_APPLE);
		if (strstr(vendorName, "Nokia"))
			rsc->setVendor(GPU_NOKIA);
		else
			rsc->setVendor(GPU_UNKNOWN);
		
		rsc->setCapability(RSC_FIXED_FUNCTION);
		
		GLint units;
		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &units);
		rsc->setNumTextureUnits(units);
		
		GLint stencil;
		glGetIntegerv(GL_STENCIL_BITS, &stencil);
		GL_CHECK_ERROR;
		
		if (stencil)
		{
			rsc->setCapability(RSC_HWSTENCIL);
			rsc->setStencilBufferBitDepth(stencil);
		}
		
		rsc->setCapability(RSC_SCISSOR_TEST);
		
		rsc->setCapability(RSC_VBO);
		
		if (mGLSupport->checkExtension("GL_IMG_texture_compression_pvrtc") ||
			mGLSupport->checkExtension("GL_AMD_compression_3DC_texture") ||
			mGLSupport->checkExtension("GL_AMD_compression_ATC_texture") ||
			mGLSupport->checkExtension("GL_OES_compressed_ETC1_RGB8_texture") ||
			mGLSupport->checkExtension("GL_OES_compressed_paletted_texture")
			)
		{
			rsc->setCapability(RSC_TEXTURE_COMPRESSION);
			
			if (mGLSupport->checkExtension("GL_IMG_texture_compression_pvrtc"))
				rsc->setCapability(RSC_TEXTURE_COMPRESSION_PVRTC);
		}
		
		if (mGLSupport->checkExtension("GL_EXT_texture_filter_anisotropic"))
			rsc->setCapability(RSC_ANISOTROPY);
			
		if (mGLSupport->checkExtension("GL_OES_framebuffer_object"))
		{
			rsc->setCapability(RSC_FBO);
			rsc->setCapability(RSC_HWRENDER_TO_TEXTURE);
		}
		else
		{
			rsc->setCapability(RSC_PBUFFER);
			rsc->setCapability(RSC_HWRENDER_TO_TEXTURE);
		}
		
		if (mGLSupport->checkExtension("GL_OES_texture_cube_map"))
			rsc->setCapability(RSC_CUBEMAPPING);
			
		if (mGLSupport->checkExtension("GL_OES_stencil_wrap"))
			rsc->setCapability(RSC_STENCIL_WRAP);
			
		if (mGLSupport->checkExtension("GL_OES_blend_subtract"))
			rsc->setCapability(RSC_ADVANCED_BLEND_OPERATIONS);
			
		rsc->setCapability(RSC_USER_CLIP_PLANES);
		
		if (mGLSupport->checkExtension("GL_OES_texture3D"))
			rsc->setCapability(RSC_TEXTURE_3D);
			
		rsc->setVertexTextureUnitsShared(true);
		
		rsc->setCapability(RSC_AUTOMIPMAP);
		
		if (mGLSupport->checkExtension("GL_EXT_texture_lod_bias"))
			rsc->setCapability(RSC_MIPMAP_LOD_BIAS);
			
		rsc->setCapability(RSC_BLENDING);
		
		rsc->setCapability(RSC_DOT3);
		
		float ps = 0;
		glGetFloatv(GL_POINT_SIZE_MAX, &ps);
		GL_CHECK_ERROR;
		rsc->setMaxPointSize(ps);
		
		if (mGLSupport->checkExtension("GL_OES_point_sprite"))
			rsc->setCapability(RSC_POINT_SPRITES);
		rsc->setCapability(RSC_POINT_EXTENDED_PARAMETERS);
		
		rsc->setCapability(RSC_VERTEX_FORMAT_UBYTE4);
		
		rsc->setCapability(RSC_INFINITE_FAR_PLANE);
		
		rsc->setCapability(RSC_HWOCCLUSION);
		
		if (mGLSupport->checkExtension("GL_OES_texture_half_float"))
			rsc->setCapability(RSC_TEXTURE_FLOAT);
		
		rsc->setCapability(RSC_ALPHA_TO_COVERAGE);
		
		return rsc;
	}
	
	void GLESRenderSystem::initialiseFromRenderSystemCapabilities(RenderSystemCapabilities* caps, RenderTarget* primary)
	{
		if (caps->getRenderSystemName() != getName())
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"Trying to initialize GLESRenderSystem from RenderSystemCapabilities that do not support OpenGL ES",
						"GLESRenderSystem::initialiseFromRenderSystemCapabilities");
		}
		
		mGpuProgramManager = OGRE_NEW GLESGpuProgramManager();
		
		mFixedFunctionTextureUnits = caps->getNumTextureUnits();
		
		if (caps->hasCapability(RSC_VBO))
		{
			mHardwareBufferManager = OGRE_NEW GLESHardwareBufferManager;
		}
		else
		{
			mHardwareBufferManager = OGRE_NEW GLESDefaultHardwareBufferManager;
		}
		
		if (caps->hasCapability(RSC_FBO))
		{
			if (caps->hasCapability(RSC_HWRENDER_TO_TEXTURE))
			{
				LogManager::getSingleton().logMessage("GL ES: Using GL_OES_framebuffer_object for rendering to textures (best)");
				mRTTManager = OGRE_NEW_FIX_FOR_WIN32 GLESFBOManager();
			}
		}
		else
		{
			if (caps->hasCapability(RSC_PBUFFER))
			{
				if (caps->hasCapability(RSC_HWRENDER_TO_TEXTURE))
				{
					mRTTManager = OGRE_NEW_FIX_FOR_WIN32 GLESPBRTTManager(mGLSupport, primary);
					LogManager::getSingleton().logMessage("GL ES: Using PBuffers for rendering to textures");
				}
			}
			
			caps->setNumMultiRenderTargets(1);
		}
		
		Log* defaultLog = LogManager::getSingleton().getDefaultLog();
		if (defaultLog)
		{
			caps->log(defaultLog);
		}
		
		mTextureManager = OGRE_NEW GLESTextureManager(*mGLSupport);
		GL_CHECK_ERROR;
		mGLInitialised = true;
	}
	
	void GLESRenderSystem::reinitialise(void)
	{
		this->shutdown();
		this->_initialise(true);
	}
	
	void GLESRenderSystem::shutdown(void)
	{
		RenderSystem::shutdown();
		
		OGRE_DELETE mGpuProgramManager;
		mGpuProgramManager = 0;
		
		OGRE_DELETE mHardwareBufferManager;
		mHardwareBufferManager = 0;
		
		OGRE_DELETE mRTTManager;
		mRTTManager = 0;
		
		mGLSupport->stop();
		
		OGRE_DELETE mTextureManager;
		mTextureManager = 0;
		
		mGLInitialised = 0;
	}
	
	void GLESRenderSystem::setAmbientLight(float r, float g, float b)
	{
		GLfloat lmodel_ambient[] = {r, g, b, 1.0};
		glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::setShadingType(ShadeOptions so)
	{
		switch(so)
		{
			case SO_FLAT:
				glShadeModel(GL_FLAT);
				GL_CHECK_ERROR;
				break;
			default:
				glShadeModel(GL_SMOOTH);
				GL_CHECK_ERROR;
				break;
		}
	}
	
	void GLESRenderSystem::setLightingEnabled(bool enabled)
	{
		if (enabled)
		{
			glEnable(GL_LIGHTING);
			GL_CHECK_ERROR;
		}
		else
		{
			glDisable(GL_LIGHTING);
			GL_CHECK_ERROR;
		}
	}
	
	RenderWindow* GLESRenderSystem::_createRenderWindow(const String& name, unsigned int width, unsigned int height,
														bool fullScreen, const NameValuePairList* miscParams)
	{
		if (mRenderTargets.find(name) != mRenderTargets.end())
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
						"NativeWindowType with name '" + name + "' already exists",
						"GLESRenderSystem::_createRenderWindow");
		}
		
		StringStream ss;
		ss << "GLESRenderSystem::_createRenderWindow \"" << name << "\", " <<
			width << "x" << height << " ";
		if (fullScreen)
			ss << "fullscreen ";
		else
			ss << "windowed ";
		
		if (miscParams)
		{
			ss << " miscParams: ";
			NameValuePairList::const_iterator it;
			for (it = miscParams->begin(); it != miscParams->end(); ++it)
			{
				ss << it->first << "=" << it->second << " ";
			}
			
			LogManager::getSingleton().logMessage(ss.str());
		}
		
		RenderWindow* win = mGLSupport->newWindow(name, width, height, fullScreen, miscParams);
		attachRenderTarget((Ogre::RenderTarget&)*win);
		
		if (!mGLInitialised)
		{
			initialiseContext(win);
			
			StringVector tokens = StringUtil::split(mGLSupport->getGLVersion(), ".");
			if (!tokens.empty())
			{
				mDriverVersion.major = StringConverter::parseInt(tokens[0]);
				if (tokens.size() > 1)
					mDriverVersion.minor = StringConverter::parseInt(tokens[1]);
				if (tokens.size() > 2)
					mDriverVersion.release = StringConverter::parseInt(tokens[2]);
			}
			mDriverVersion.build = 0;
			mRealCapabilities = createRenderSystemCapabilities();
			
			if (!mUseCustomCapabilities)
				mCurrentCapabilities = mRealCapabilities;
			
			initialiseFromRenderSystemCapabilities(mCurrentCapabilities, (RenderTarget*)win);
			
			_oneTimeContextInitialization();
			if (mCurrentContext)
				mCurrentContext->setInitialized();
		}
		
		return win;
	}
	
	MultiRenderTarget* GLESRenderSystem::createMultiRenderTarget(const String& name)
	{
		MultiRenderTarget* retval = mRTTManager->createMultiRenderTarget(name);
		attachRenderTarget(*retval);
		return retval;
	}
	
	void GLESRenderSystem::destroyRenderWindow(RenderWindow* pWin)
	{
		RenderTargetMap::iterator i = mRenderTargets.begin();
		
		while (i != mRenderTargets.end())
		{
			if (i->second == pWin)
			{
				mRenderTargets.erase(i);
				OGRE_DELETE pWin;
				break;
			}
		}
	}
	
	String GLESRenderSystem::getErrorDescription(long errorNumber) const
	{
		return StringUtil::BLANK;
	}
	
	VertexElementType GLESRenderSystem::getColourVertexElementType(void) const
	{
		return VET_COLOUR_ABGR;
	}
	
	void GLESRenderSystem::setNormaliseNormals(bool normalise)
	{
		if (normalise)
			glEnable(GL_NORMALIZE);
		else
			glDisable(GL_NORMALIZE);
			
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_useLights(const LightList& lights, unsigned short limit)
	{
		glMatrixMode(GL_MODELVIEW);
		GL_CHECK_ERROR;
		glPushMatrix();
		GL_CHECK_ERROR;
		
		GLfloat mat[16];
		makeGLMatrix(mat, mViewMatrix);
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
		
		LightList::const_iterator i, iend;
		iend = lights.end();
		unsigned short num = 0;
		for (i = lights.begin(); i != iend && num < limit; ++i, ++num)
		{
			setGLLight(num, *i);
			mLights[num] = *i;
		}
		for (; num < mCurrentLights; ++num)
		{
			setGLLight(num, NULL);
			mLights[num] = NULL;
		}
		mCurrentLights = std::min(limit, static_cast<unsigned short>(lights.size()));
		
		setLights();
		
		glPopMatrix();
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_setWorldMatrix(const Matrix4& m)
	{
		GLfloat mat[16];
		mWorldMatrix = m;
		makeGLMatrix(mat, mViewMatrix * mWorldMatrix);
		glMatrixMode(GL_MODELVIEW);
		GL_CHECK_ERROR;
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_setViewMatrix(const Matrix4& m)
	{
		mViewMatrix = m;
		
		GLfloat mat[16];
		makeGLMatrix(mat, mViewMatrix * mWorldMatrix);
		glMatrixMode(GL_MODELVIEW);
		GL_CHECK_ERROR;
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
		
		if (!mClipPlanes.empty())
		{
			mClipPlanesDirty = true;
		}
	}
	
	void GLESRenderSystem::_setProjectionMatrix(const Matrix4& m)
	{
		GLfloat mat[16];
		makeGLMatrix(mat, m);
		if (mActiveRenderTarget->requiresTextureFlipping())
		{
			mat[1] = -mat[1];
			mat[5] = -mat[5];
			mat[9] = -mat[9];
			mat[13] = -mat[13];
		}
		glMatrixMode(GL_PROJECTION);
		GL_CHECK_ERROR;
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
		
		glMatrixMode(GL_MODELVIEW);
		GL_CHECK_ERROR;
		
		if (!mClipPlanes.empty())
			mClipPlanesDirty = true;
	}
	
	void GLESRenderSystem::_setSurfaceParams(const ColourValue& ambient,
											 const ColourValue& diffuse,
											 const ColourValue& specular,
											 const ColourValue& emissive,
											 Real shininess,
											 TrackVertexColourType tracking)
	{
		if (tracking != TVC_NONE)
		{
			GLenum gt = GL_DIFFUSE;
			if (tracking & TVC_AMBIENT)
			{
				if (tracking & TVC_DIFFUSE)
				{
					gt = GL_AMBIENT_AND_DIFFUSE;
				}
				else
				{
					gt = GL_AMBIENT;
				}
			}
			else if (tracking & TVC_DIFFUSE)
			{
				gt = GL_DIFFUSE;
			}
			else if (tracking & TVC_SPECULAR)
			{
				gt = GL_SPECULAR;
			}
			else if (tracking & TVC_EMISSIVE)
			{
				gt = GL_EMISSION;
			}
			glEnable(gt);
			GL_CHECK_ERROR;
			glEnable(GL_COLOR_MATERIAL);
			GL_CHECK_ERROR;
		}
		else
		{
			glDisable(GL_COLOR_MATERIAL);
		}
		
		GLfloat f4val[4] = {diffuse.r, diffuse.g, diffuse.b, diffuse.a};
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, f4val);
		GL_CHECK_ERROR;
		f4val[0] = ambient.r;
		f4val[1] = ambient.g;
		f4val[2] = ambient.b;
		f4val[3] = ambient.a;
		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, f4val);
		GL_CHECK_ERROR;
		f4val[0] = specular.r;
		f4val[1] = specular.g;
		f4val[2] = specular.b;
		f4val[3] = specular.a;
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, f4val);
		GL_CHECK_ERROR;
		f4val[0] = emissive.r;
		f4val[1] = emissive.g;
		f4val[2] = emissive.b;
		f4val[3] = emissive.a;
		glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, f4val);
		GL_CHECK_ERROR;
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_setPointParameters(Real size,
											   bool attenuationEnabled,
											   Real constant,
											   Real linear,
											   Real quadratic,
											   Real minSize,
											   Real maxSize)
	{
		GL_CHECK_ERROR;
		if (attenuationEnabled &&
			mCurrentCapabilities->hasCapability(RSC_POINT_EXTENDED_PARAMETERS))
		{
			Real adjSize = size * mActiveViewport->getActualHeight();
			Real adjMinSize = minSize * mActiveViewport->getActualHeight();
			Real adjMaxSize;
			if (maxSize == 0.0f)
				adjMaxSize = mCurrentCapabilities->getMaxPointSize();
			else
				adjMaxSize = maxSize * mActiveViewport->getActualHeight();
				
			glPointSize(adjSize);
			GL_CHECK_ERROR;
			
			Real correction = 0.005;
			float val[4] = {constant, linear * correction,
							quadratic * correction, 1};
			glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, val);
			GL_CHECK_ERROR;
			glPointParameterf(GL_POINT_SIZE_MIN, adjMinSize);
			GL_CHECK_ERROR;
			glPointParameterf(GL_POINT_SIZE_MAX, adjMaxSize);
			GL_CHECK_ERROR;
		}
		else
		{
			glPointSize(size);
			GL_CHECK_ERROR;
			
			if (mCurrentCapabilities->hasCapability(RSC_POINT_EXTENDED_PARAMETERS))
			{
				float val[4] = {1, 0, 0, 1};
				glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, val);
				GL_CHECK_ERROR;
				glPointParameterf(GL_POINT_SIZE_MIN, minSize);
				GL_CHECK_ERROR;
				if (maxSize == 0.0f)
				{
					maxSize = mCurrentCapabilities->getMaxPointSize();
				}
				glPointParameterf(GL_POINT_SIZE_MAX, maxSize);
				GL_CHECK_ERROR;
			}
		}
	}
	
	void GLESRenderSystem::_setPointSpritesEnabled(bool enabled)
	{
		if (!getCapabilities()->hasCapability(RSC_POINT_SPRITES))
			return;
			
		GL_CHECK_ERROR;
		if (enabled)
		{
			glEnable(GL_POINT_SPRITE_OES);
			GL_CHECK_ERROR;
		}
		else
		{
			glDisable(GL_POINT_SPRITE_OES);
			GL_CHECK_ERROR;
		}
		
		for (ushort i = 0; i < mFixedFunctionTextureUnits; ++i)
		{
			activeGLTextureUnit(i);
			glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES,
					enabled ? GL_TRUE : GL_FALSE);
		}
		activeGLTextureUnit(i);
	}
	
	void GLESRenderSystem::_setTexture(size_t stage, bool enabled, const TexturePtr& texPtr)
	{
		GL_CHECK_ERROR;
		
		GLESTexturePtr tex = texPtr;
		
		if (!activateGLTextureUnit(stage))
			return;
			
		if (enabled)
		{
			if (!tex.isNull())
			{
				tex->touch();
			}
			
			glEnable(GL_TEXTURE_2D);
			GL_CHECK_ERROR;
			
			mTextureMipmapCount = tex->getNumMipmaps();
			
			if (!tex.isNull())
			{
				glBindTexture(GL_TEXTURE_2D, tex->getGLID);
				GL_CHECK_ERROR;
			}
			else
			{
				glBindTexture(GL_TEXTURE_2D, static_cast<GLESTextureManager*>(mTextureManager)->getWarningTextureID());
				GL_CHECK_ERROR;
			}
		}
		else
		{
			glEnable(GL_TEXTURE_2D);
			glDisable(GL_TEXTURE_2D);
			GL_CHECK_ERROR;
			
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			GL_CHECK_ERROR;
			
			glBindTexture(GL_TEXTURE_2D, 0);
			GL_CHECK_ERROR;
		}
		
		activateGLTextureUnit(0);
	}
	
	void GLESRenderSystem::_setTextureCoordSet(size_t stage, size_t index)
	{
		mTextureCoordIndex[stage] = index;
	}
	
	void GLESRenderSystem::_setTextureCoordCalculation(size_t stage,
													   TexCoordCalcMethod m,
													   const Frustum* frustum)
	{
		if (stage >= mFixedFunctionTextureUnits)
		{
			return;
		}
		
		GLfloat M[16];
		Matrix4 projectionBias;
		
		mUseAutoTextureMatrix = false;
		
		if (!activateGLTextureUnit(stage))
			return;
		switch(m)
		{
			case TEXCALC_NONE:
				break;
				
			case TEXCALC_ENVIRONMENT_MAP:
				mUseAutoTextureMatrix = true;
				memset(mAutoTextureMatrix, 0, sizeof(GLfloat) * 16);
				mAutoTextureMatrix[0] = mAutoTextureMatrix[10] = mAutoTextureMatrix[15] = 1.0f;
				mAutoTextureMatrix[5] = -1.0f;
				break;
				
			case TEXCALC_ENVIRONMENT_MAP_PLANAR:
				break;
				
			case TEXCALC_ENVIRONMENT_MAP_REFLECTION:
				mUseAutoTextureMatrix = true;
				makeGLMatrix(M, mViewMatrix);
				
				mAutoTextureMatrix[0] = M[0]; mAutoTextureMatrix[1] = M[4]; mAutoTextureMatrix[2] = -M[8];
				mAutoTextureMatrix[4] = M[1]; mAutoTextureMatrix[5] = M[5]; mAutoTextureMatrix[6] = -M[9];
				mAutoTextureMatrix[8] = M[2]; mAutoTextureMatrix[9] = M[6]; mAutoTextureMatrix[10] = -M[10];
				mAutoTextureMatrix[3] = mAutoTextureMatrix[7] = mAutoTextureMatrix[11] = 0.0f;
				mAutoTextureMatrix[12] = mAutoTextureMatrix[13] = mAutoTextureMatrix[14] = 0.0f;
				mAutoTextureMatrix[15] = 1.0f;
				break;
				
			case TEXCALC_ENVIRONMENT_MAP_NORMAL:
				break;
				
			case TEXCALC_PROJECTION_TEXTURE:
				mUseAutoTextureMatrix = true;
				
				projectionBias = Matrix4::CLIPSPACE2DTOIMAGESPACE;
				
				projectionBias = projectionBias * frustum->getProjectionMatrix();
				projectionBias = projectionBias * frustum->getViewMatrix();
				projectionBias = projectionBias * mWorldMatrix;
				
				makeGLMatrix(mAutoTextureMatrix, projectionBias);
				break;
				
			default:
				break;
		}
		
		activateGLTextureUnit(0);
	}
	
	void GLESRenderSystem::_setTextureBlendMode(size_t stage, const LayerBlendModeEx& bm)
	{
		if (stage >= mFixedFunctionTextureUnits)
		{
			return;
		}
		
		if (!mCurrentCapabilities->hasCapability(RSC_BLENDING))
			return;
			
		GLenum src1op, src2op, cmd;
		GLfloat cv1[4], cv2[4];
		
		if (bm.blendType == LBT_COLOUR)
		{
			cv1[0] = bm.colourArg1.r;
			cv1[1] = bm.colourArg1.g;
			cv1[2] = bm.colourArg1.b;
			cv1[3] = bm.colourArg1.a;
			mManualBlendColours[stage][0] = bm.colourArg1;
			
			cv2[0] = bm.colourArg2.r;
			cv2[1] = bm.colourArg2.g;
			cv2[2] = bm.colourArg2.b;
			cv2[3] = bm.colourArg2.a;
			mManualBlendColours[stage][1] = bm.colourArg2;
		}
		
		if (bm.blendType == LBT_ALPHA)
		{
			cv1[0] = mManualBlendColours[stage][0].r;
			cv1[1] = mManualBlendColours[stage][0].g;
			cv1[2] = mManualBlendColours[stage][0].b;
			cv1[3] = bm.alphaArg1;
			
			cv2[0] = mManualBlendColours[stage][1].r;
			cv2[1] = mManualBlendColours[stage][1].g;
			cv2[2] = mManualBlendColours[stage][1].b;
			cv2[3] = bm.alphaArg2;
		}
		
		switch(bm.source1)
		{
			case LBS_CURRENT:
				src1op = GL_PREVIOUS;
				break;
			case LBS_TEXTURE:
				src1op = GL_TEXTURE;
				break;
			case LBS_MANUAL:
				src1op = GL_CONSTANT;
				break;
			case LBS_DIFFUSE:
				src1op = GL_PRIMARY_COLOR;
				break;
			case LBS_SPECULAR:
				src1op = GL_PRIMARY_COLOR;
				break;
			default:
				src1op = 0;
		}
		
		switch(bm.source2)
		{
			case LBS_CURRENT:
				src2op = GL_PREVIOUS;
				break;
			case LBS_TEXTURE:
				src2op = GL_TEXTURE;
				break;
			case LBS_MANUAL:
				src2op = GL_CONSTANT;
				break;
			case LBS_DIFFUSE:
				src2op = GL_PRIMARY_COLOR;
				break;
			case LBS_SPECULAR:
				src2op = GL_PRIMARY_COLOR;
				break;
			default:
				src2op = 0;
		}
		
		switch(bm.operation)
		{
			case LBX_SOURCE1:
				cmd = GL_REPLACE;
				break;
			case LBX_SOURCE2:
				cmd = GL_REPLACE;
				break;
			case LBX_MODULATE:
				cmd = GL_MODULATE;
				break;
			case LBX_MODULATE_X2:
				cmd = GL_MODULATE;
				break;
			case LBX_MODULATE_X4:
				cmd = GL_MODULATE;
				break;
			case LBX_ADD:
				cmd = GL_ADD;
				break;
			case LBX_ADD_SIGNED:
				cmd = GL_ADD_SIGNED;
				break;
			case LBX_ADD_SMOOTH:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_SUBTRACT:
				cmd = GL_SUBTRACT;
				break;
			case LBX_BLEND_DIFFUSE_COLOUR:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_BLEND_DIFFUSE_ALPHA:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_BLEND_TEXTURE_ALPHA:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_BLEND_CURRENT_ALPHA:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_BLEND_MANUAL:
				cmd = GL_INTERPOLATE;
				break;
			case LBX_DOTPRODUCT:
				cmd = mCurrentCapabilities->hasCapability(RSC_DOT3)
					? GL_DOT3_RGB : GL_MODULATE;
				break;
			default:
				cmd = 0;
		}
		
		if (!activateGLTextureUnit(stage))
			return;
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
		GL_CHECK_ERROR;
		
		if (bm.blendType == LBT_COLOUR)
		{
			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, cmd);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, src1op);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, src2op);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
			GL_CHECK_ERROR;
		}
		else
		{
			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, cmd);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, src1op);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, src2op);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_CONSTANT);
			GL_CHECK_ERROR;
		}
		
		float blendValue[4] = {0, 0, 0, bm.factor};
		switch(bm.operation)
		{
			case LBX_BLEND_DIFFUSE_COLOUR:
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PRIMARY_COLOR);
				GL_CHECK_ERROR;
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_RPIMARY_COLOR);
				GL_CHECK_ERROR;
				break;
			case LBX_BLEND_DIFFUSE_ALPHA:
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PRIMARY_COLOR);
				GL_CHECK_ERROR;
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_PRIMARY_COLOR);
				GL_CHECK_ERROR;
				break;
			case LBX_BLEND_TEXTURE_ALPHA:
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE);
				GL_CHECK_ERROR;
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_TEXTURE);
				GL_CHECK_ERROR;
				break;
			case LBX_BLEND_CURRENT_ALPHA:
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PREVIOUS);
				GL_CHECK_ERROR;
				glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_ALPHA, GL_PREVIOUS);
				GL_CHECK_ERROR;
				break;
			case LBX_BLEND_MANUAL:
				glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, blendValue);
				GL_CHECK_ERROR;
				break;
			default:
				break;
		};
		
		switch(bm.operation)
		{
			case LBX_MODULATE_X2:
				glTexEnvi(GL_TEXTURE_ENV, bm.blendType == LBT_COLOUR ?
						GL_RGB_SCALE : GL_ALPHA_SCALE, 2);
				GL_CHECK_ERROR;
				break;
			case LBX_MODULATE_X4:
				glTexEnvi(GL_TEXTURE_ENV, bm.blendType == LBT_COLOUR ?
						GL_RGB_SCALE : GL_ALPHA_SCALE, 4);
				GL_CHECK_ERROR;
				break;
			default:
				glTexEnvi(GL_TEXTURE_ENV, bm.blendType == LBT_COLOUR ?
						GL_RGB_SCALE : GL_ALPHA_SCALE, 1);
				GL_CHECK_ERROR;
				break;
		}
		
		if (bm.blendType == LBT_COLOUR)
		{
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
			GL_CHECK_ERROR;
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
			GL_CHECK_ERROR;
			if (bm.operation == LBX_BLEND_DIFFUSE_COLOUR)
			{
				glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
				GL_CHECK_ERROR;
			}
			else
			{
				glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA);
				GL_CHECK_ERROR;
			}
		}
		
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
		GL_CHECK_ERROR;
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
		GL_CHECK_ERROR;
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA, GL_SRC_ALPHA);
		GL_CHECK_ERROR;
		if (bm.source1 == LBS_MANUAL)
			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, cv1);
		if (bm.source2 == LBS_MANUAL)
			glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, cv2);
			
		activateGLTextureUnit(0);
	}
	
	GLint GLESRenderSystem::getTextureAddressingMode(TextureUnitState::TextureAddressingMode tam) const
	{
		switch(tam)
		{
			case TextureUnitState::TAM_CLAMP:
			case TextureUnitState::TAM_BORDER:
				return GL_CLAMP_TO_EDGE;
			case TextureUnitState::TAM_MIRROR:
#if GL_OES_texture_mirrored_repeat
				return GL_MIRRORED_REPEAT_OES;
#endif
			case TextureUnitState::TAM_WRAP:
			default:
				return GL_REPEAT;
		}
	}
	
	void GLESRenderSystem::_setTextureAddressingMode(size_t stage, const TextureUnitState::UVWAddressingMode& uvw)
	{
		if (!activateGLTextureUnit(stage))
			return;
		glTexParameteri(GL_TEXTURE_2D,
						GL_TEXTURE_WRAP_S, getTextureAddressingMode(uvw.u));
		GL_CHECK_ERROR;
		glTexParameteri(GL_TEXTURE_2D,
						GL_TEXTURE_WRAP_T, getTextureAddressingMode(uvw.v));
		GL_CHECK_ERROR;
		activateGLTextureUnit(0);
	}
	
	void GLESRenderSystem::_setTextureBorderColour(size_t stage, const ColourValue& colour)
	{
		
	}
	
	void GLESRenderSystem::_setTextureMipmapBias(size_t unit, float bias)
	{
		if (mCurrentCapabilities->hasCapability(RSC_MIPMAP_LOD_BIAS))
		{
#if GL_EXT_texture_lod_bias
			if (activateGLTextureUnit(unit))
			{
				glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, bias);
				activateGLTextureUnit(0);
			}
#endif
		}
	}
	
	void GLESRenderSystem::_setTextureMatrix(size_t stage, const Matrix4& xform)
	{
		if (stage >= mFixedFunctionTextureUnits)
		{
			return;
		}
		
		if (!activateGLTextureUnit(stage))
			return;
			
		GLfloat mat[16];
		makeGLMatrix(mat, xform);
		
		glMatrixMode(GL_TEXTURE);
		GL_CHECK_ERROR;
		
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
		
		if (mUseAutoTextureMatrix)
		{
			glMultMatrixf(mAutoTextureMatrix);
		}
		
		glMatrixMode(GL_MODELVIEW);
		GL_CHECK_ERROR;
		activateGLTextureUnit(0);
	}
	
	GLint GLESRenderSystem::getBlendMode(SceneBlendFactor ogreBlend) const
	{
		switch(ogreBlend)
		{
			case SBF_ONE:
				return GL_ONE;
			case SBF_ZERO:
				return GL_ZERO;
			case SBF_DEST_COLOUR:
				return GL_DST_COLOR;
			case SBF_SOURCE_COLOUR:
				return GL_SRC_COLOR:
			case SBF_ONE_MINUS_DEST_COLOUR:
				return GL_ONE_MINUS_DST_COLOR;
			case SBF_ONE_MINUS_SOURCE_COLOUR:
				return GL_ONE_MINUS_SRC_COLOR;
			case SBF_DEST_ALPHA:
				return GL_DST_ALPHA;
			case SBF_SOURCE_ALPHA:
				return GL_SRC_ALPHA;
			case SBF_ONE_MINUS_DEST_ALPHA:
				return GL_ONE_MINUS_DST_ALPHA;
			case SBF_ONE_MINUS_SOURCE_ALPHA:
				return GL_ONE_MINUS_SRC_ALPHA;
		};
		
		return GL_NONE;
	}
	
	void GLESRenderSystem::_setSceneBlending(SceneBlendFactor sourceFactor, SceneBlendFactor destFactor, SceneBlendOperation op)
	{
		GL_CHECK_ERROR;
		GLint sourceBlend = getBlendMode(sourceFactor);
		GLint destBlend = getBlendMode(destFactor);
		if (sourceFactor == SBF_ONE && destFactor == SBF_ZERO)
		{
			glDisable(GL_BLEND);
			GL_CHECK_ERROR;
		}
		else
		{
			if (sourceFactor == SBF_SOURCE_COLOUR)
			{
				sourceBlend = getBlendMode(SBF_SOURCE_ALPHA);
			}
			glEnable(GL_BLEND);
			GL_CHECK_ERROR;
			glBlendFunc(sourceBlend, destBlend);
			GL_CHECK_ERROR;
		}
		
#if GL_OES_blend_subtract
		GLint func = GL_FUNC_ADD_OES;
		switch(op)
		{
			case SBO_ADD:
				func = GL_FUNC_ADD_OES;
				break;
			case SBO_SUBTRACT:
				func = GL_FUNC_SUBTRACT_OES;
				break;
			case SBO_REVERSE_SUBTRACT:
				func = GL_FUNC_REVERSE_SUBTRACT_OES;
				break;
			case SBO_MIN:
#if GL_EXT_blend_minmax
				func = GL_MIN_EXT;
#endif
				break;
			case SBO_MAX:
#if GL_EXT_blend_minmax
				func = GL_MAX_EXT;
#endif
				break;
		}
		if (glBlendEquationOES)
			glBlendEquationOES(func);
		GL_CHECK_ERROR;
#endif
	}
	
	void GLESRenderSystem::_setSeparateSceneBlending(
			SceneBlendFactor sourceFactor, SceneBlendFactor destFactor,
			SceneBlendFactor sourceFactorAlpha, SceneBlendFactor destFactorAlpha,
			SceneBlendOperation op, SceneBlendOperation alphaOp)
	{
#if defined(GL_OES_blend_func_separate) && defined(GL_OES_blend_equation_separate) && defined(GL_EXT_blend_minmax)
		if (mGLSupport->checkExtension("GL_OES_blend_equation_separate") &&
			mGLSupport->checkExtension("GL_OES_blend_func_separate"))
		{
			GLint sourceBlend = getBlendMode(sourceFactor);
			GLint destBlend = getBlendMode(destFactor);
			GLint sourceBlendAlpha = getBlendMode(sourceFactorAlpha);
			GLint destBlendAlpha = getBlendMode(destFactorAlpha);
			
			if (sourceFactor == SBF_ONE && destFactor == SBF_ZERO &&
				sourceFactorAlpha == SBF_ONE && destFactorAlpha == SBF_ZERO)
			{
				glDisable(GL_BLEND);
			}
			else
			{
				glEnable(GL_BLEND);
				GL_CHECK_ERROR;
				glBlendFuncSeparateOES(sourceBlend, destBlend, sourceBlendAlpha, destBlendAlpha);
				GL_CHECK_ERROR; 
			}
			
			GLint func = GL_FUNC_ADD_OES, alphaFunc = GL_FUNC_ADD_OES;
			
			switch(op)
			{
				case SBO_ADD:
					func = GL_FUNC_ADD_OES;
					break;
				case SBO_SUBTRACT:
					func = GL_FUNC_SUBTRACT_OES;
					break;
				case SBO_REVERSE_SUBTRACT:
					func = GL_FUNC_REVERSE_SUBTRACT_OES;
					break;
				case SBO_MIN:
					func = GL_MIN_EXT;
					break;
				case SBO_MAX:
					func = GL_MAX_EXT;
					break;
			}
			
			switch(alphaOp)
			{
				case SBO_ADD:
					alphaFunc = GL_FUNC_ADD_OES;
					break;
				case SBO_SUBTRACT:
					alphaFunc = GL_FUNC_SUBTRACT_OES;
					break;
				case SBO_REVERSE_SUBTRACT:
					alphaFunc = GL_FUNC_REVERSE_SUBTRACT_OES;
					break;
				case SBO_MIN:
					alphaFunc = GL_MIN_EXT;
					break;
				case SBO_MAX:
					alphaFunc = GL_MAX_EXT;
					break;
			}
			
			glBlendEquationSeparateOES(func, alphaFunc);
			GL_CHECK_ERROR;
		}
#endif
	}
	
	void GLESRenderSystem::_setAlphaRejectSettings(CompareFunction func, unsigned char value, bool alphaToCoverage)
	{
		bool a2c = false;
		static bool lasta2c = false;
		if (func == CMPF_ALWAYS_PASS)
		{
			glDisable(GL_ALPHA_TEST);
			GL_CHECK_ERROR;
		}
		else
		{
			glEnable(GL_ALPHA_TEST);
			GL_CHECK_ERROR;
			
			a2c = alphaToCoverage;
			
			glAlphaFunc(convertCompareFunction(func), value / 255.0f);
			GL_CHECK_ERROR;
		}
		if (a2c != lasta2c && getCapabilities()->hasCapability(RSC_ALPHA_TO_CONVERAGE))
		{
			if (a2c)
				glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
			else
				glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
				
			GL_CHECK_ERROR;
			
			lasta2c = a2c;
		}
	}
	
	void GLESRenderSystem::_setViewport(Viewport* vp)
	{
		if (vp != mActiveViewport || vp->_isUpdated())
		{
			RenderTarget* target;
			
			target = vp->getTarget();
			_setRenderTarget(target);
			mActiveViewport = vp;
			
			GLsizei x, y, w, h;
			w = vp->getActualWidth();
			h = vp->getActualHeight();
			x = vp->getActualLeft();
			y = vp->getActualTop();
			
			if (!target->requiresTextureFlipping())
			{
				y = target->getHeight() - h - y;
			}
			
			glViewport(x, y, w, h);
			GL_CHECK_ERROR;
			
			glScissor(x, y, w, h);
			GL_CHECK_ERROR;
			
			vp->_clearUpdatedFlag();
		}
	}
	
	void GLESRenderSystem::_beginFrame(void)
	{
		if (!mActiveViewport)
			OGRE_EXCEPT(Exception::ERR_INVALID_STATE,
						"Cannot begin Frame - no viewport selected.",
						"GLESRenderSystem::_beginFrame");
						
		if (mCurrentCapabilities->hasCapability(RSC_SCISSOR_TEST))
		{
			glEnable(GL_SCISSOR_TEST);
			GL_CHECK_ERROR;
		}
	}
	
	void GLESRenderSystem::_endFrame(void)
	{
		if (mCurrentCapabilities->hasCapability(RSC_SCISSOR_TEST))
		{
			glDisable(GL_SCISSOR_TEST);
			GL_CHECK_ERROR;
		}
	}
	
	void GLESRenderSystem::_setCullingMode(CullingMode mode)
	{
		mCullingMode = mode;
		GLenum cullMode;
		
		switch(mode)
		{
			case CULL_NONE:
				glDisable(GL_CULL_FACE);
				GL_CHECK_ERROR;
				return;
				
			default:
			case CULL_CLOCKWISE:
				if (mActiveRenderTarget &&
					((mActiveRenderTarget->requiresTextureFlipping() && !mInvertVertexWinding) ||
					(!mActiveRenderTarget->requiresTextureFlipping() && mInvertVertexWinding)))
				{
					cullMode = GL_FRONT;
				}
				else
				{
					cullMode = GL_BACK;
				}
				break;
			case CULL_ANTICLOCKWISE:
				if (mActiveRenderTarget &&
					((mActiveRenderTarget->requiresTextureFlipping() && !mInvertVertexWinding) ||
					(!mActiveRenderTarget->requiresTextureFlipping() && mInvertVertexWinding)))
				{
					cullMode = GL_BACK;
				}
				else
				{
					cullMode = GL_FRONT;
				}
				break;
		}
		
		glEnable(GL_CULL_FACE);
		GL_CHECK_ERROR;
		glCullFace(cullMode);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_setDepthBufferParams(bool depthTest, bool depthWrite, CompareFunction depthFunction)
	{
		_setDepthBufferCheckEnabled(depthTest);
		_setDepthBufferWriteEnabled(depthWrite);
		_setDepthBufferFunction(depthFunction);
	}
	
	void GLESRenderSystem::_setDepthBufferCheckEnabled(bool enabled)
	{
		if (enabled)
		{
			glClearDepthf(1.0f);
			GL_CHECK_ERROR;
			glEnable(GL_DEPTH_TEST);
			GL_CHECK_ERROR;
		}
		else
		{
			glDisable(GL_DEPTH_TEST);
			GL_CHECK_ERROR;
		}
	}
	
	void GLESRenderSystem::_setDepthBufferWriteEnabled(bool enabled)
	{
		GLboolean flag = enabled ? GL_TRUE : GL_FALSE;
		glDepthMask(flag);
		GL_CHECK_ERROR;
		
		mDepthWrite = enabled;
	}
	
	void GLESRenderSystem::_setDepthBufferFunction(CompareFunction func)
	{
		glDepthFunc(convertCompareFunction(func));
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_setDepthBias(float constantBias, float slopeScaleBias)
	{
		if (constantBias != 0 || slopeScaleBias != 0)
		{
			glEnable(GL_POLYGON_OFFSET_FILL);
			GL_CHECK_ERROR;
			glPolygonOffset(-slopScaleBias, -constantBias);
			GL_CHECK_ERROR;
		}
		else
		{
			glDisable(GL_POLYGON_OFFSET_FILL);
			GL_CHECK_ERROR;
		}
	}
	
	void GLESRenderSystem::_setColourBufferWriteEnabled(bool red, bool green, bool blue, bool alpha)
	{
		glColorMask(red, green, blue, alpha);
		GL_CHECK_ERROR;
		
		mColourWrite[0] = red;
		mColourWrite[1] = blue;
		mColourWrite[2] = green;
		mColourWrite[3] = alpha;
	}
	
	void GLESRenderSystem::_setFog(FogMode mode, const ColourValue& colour, Real density, Real start, Real end)
	{
		GLint fogMode;
		switch(mode)
		{
			case FOG_EXP:
				fogMode = GL_EXP;
				break;
			case FOG_EXP2:
				fogMode = GL_EXP2;
				break;
			case FOG_LINEAR:
				fogMode = GL_LINEAR;
				break;
			default:
				glDisable(GL_FOG);
				GL_CHECK_ERROR;
				return;
		}
		
		glEnable(GL_FOG);
		GL_CHECK_ERROR;
		glFogf(GL_FOG_MODE, fogMode);
		GLfloat fogColor[4] = {colour.r, colour.g, colour.b, colour.a};
		glFogfv(GL_FOG_COLOR, fogColor);
		GL_CHECK_ERROR;
		glFogf(GL_FOG_DENSITY, density);
		GL_CHECK_ERROR;
		glFogf(GL_FOG_START, start);
		GL_CHECK_ERROR;
		glFogf(GL_FOG_END, end);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_convertProjectionMatrix(const Matrix4& matrix,
													Matrix4& dest,
													bool forGpuProgram)
	{
		dest = matrix;
	}
	
	void GLESRenderSystem::_makeProjectionMatrix(const Radian& fovy, Real aspect,
												 Real nearPlane, Real farPlane,
												 Matrix4& dest, bool forGpuProgram)
	{
		Radian thetaY(fovy / 2.0f);
		Real tanThetaY = Math::Tan(thetaY);
		
		Real w = (1.0f / tanThetaY) / aspect;
		Real h = 1.0f / tanThetaY;
		Real q, qn;
		if (farPlane == 0)
		{
			q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
			qn = nearPlane * (Frustum::INFINITE_FAR_PLANE_ADJUST - 2);
		}
		else
		{
			q = -(farPlane + nearPlane) / (farPlane - nearPlane);
			qn = -2 * (farPlane * nearPlane) / (farPlane - nearPlane);
		}
		
		dest = Matrix4::ZERO;
		dest[0][0] = w;
		dest[1][1] = h;
		dest[2][2] = q;
		dest[2][3] = qn;
		dest[3][2] = -1;
	}
	
	void GLESRenderSystem::_makeProjectionMatrix(Real left, Real right,
												 Real bottom, Real top,
												 Real nearPlane, Real farPlane,
												 Matrix4& dest, bool forGpuProgram)
	{
		Real width = right - left;
		Real height = top - bottom;
		Real q, qn;
		if (farPlane == 0)
		{
			q = Frustum::INFINITE_FAR_PLANE_ADJUST - 1;
			qn = nearPlane * (Frustum::INFINITE_FAR_PLANE_ADJUST - 1);
		}
		else
		{
			q = -(farPlane + nearPlane) / (farPlane - nearPlane);
			qn = -2 * (farPlane * nearPlane) / (farPlane - nearPlane);
		}
		
		dest = Matrix4::ZERO;
		dest[0][0] = 2 * nearPlane / width;
		dest[0][2] = (right + left) / width;
		dest[1][1] = 2 * nearPlane / height;
		dest[1][2] = (top + bottom) / height;
		dest[2][2] = q;
		dest[2][3] = qn;
		dest[3][2] = -1;
	}
	
	void GLESRenderSystem::_makeOrthoMatrix(const Radian& fovy, Real aspect,
											Real nearPlane, Real farPlane,
											Matrix4& dest, bool forGpuProgram)
	{
		Radian thetaY(fovy / 2.0f);
		Real tanThetaY = Math::Tan(thetaY);
		
		Real tanThetaX = tanThetaY * aspect;
		Real half_w = tanThetaX * nearPlane;
		Real half_h = tanThetaY * nearPlane;
		Real iw = 1.0 / half_w;
		Real ih = 1.0 / half_h;
		Real g;
		if (farPlane == 0)
		{
			q = 0;
		}
		else
		{
			q = 2.0 / (farPlane - nearPlane);
		}
		dest = Matrix4::ZERO;
		dest[0][0] = iw;
		dest[1][1] = ih;
		dest[2][2] = -q;
		dest[2][3] = -(farPlane + nearPlane) / (farPlane - nearPlane);
		dest[3][3] = 1;
	}
	
	void GLESRenderSystem::_applyObliqueDepthProjection(Matrix4& matrix,
														const Plane& plane,
														bool forGpuProgram)
	{
		Vector4 q;
        q.x = (Math::Sign(plane.normal.x) + matrix[0][2]) / matrix[0][0];
        q.y = (Math::Sign(plane.normal.y) + matrix[1][2]) / matrix[1][1];
		q.z = -1.0F;
		q.w = (1.0F + matrix[2][2] / matrix[2][3];
		
		Vector4 clipPlane4d(plane.normal.x, plane.normal.y, plane.normal.z, plane.d);
        Vector4 c = clipPlane4d * (2.0F / (clipPlane4d.dotProduct(q)));
		
		matrix[2][0] = c.x;
		matrix[2][1] = c.y;
		matrix[2][2] = c.z + 1.0F;
		matrix[2][3] = c.w;
	}
	
	void GLESRenderSystem::_setPolygonMode(PolygonMode level)
	{
		
	}
	
	void GLESRenderSystem::setStencilCheckEnabled(bool enabled)
	{
		if (enabled)
		{
			glEnable(GL_STENCIL_TEST);
		}
		else
		{
			glDisable(GL_STENCIL_TEST);
		}
	}
	
	void GLESRenderSystem::setStencilBufferParams(CompareFunction func,
												  uint32 refValue, uint32 mask,
												  StencilOperation stencilFailOp,
												  StencilOperation depthFailOp,
												  StencilOperation passOp,
												  bool twoSidedOperation)
	{
		bool flip;
		mStencilMask = mask;
		
		flip = (mInvertVertexWinding && !mActiveRenderTarget->requiresTextureFlipping()) ||
			(!mInvertVertexWinding && mActiveRenderTarget->requiresTextureFlipping());
		
		flip = false;
		glStencilMask(mask);
		glStencilFunc(convertCompareFunction(func), refValue, mask);
		glStencilOp(
				convertStencilOp(stencilFailOp, flip),
				convertStencilOp(depthFailOp, flip),
				convertStencilOp(passOp, flip));
	}
	
	GLuint GLESRenderSystem::getCombinedMinMipFilter(void) const
	{
		switch(mMinFilter)
		{
			case FO_ANISOTROPIC:
			case FO_LINEAR:
				switch(mMipFilter)
				{
					case FO_ANISOTROPIC:
					case FO_LINEAR:
						return GL_LINEAR_MIPMAP_LINEAR;
					case FO_POINT:
						return GL_LINEAR_MIPMAP_NEAREST;
					case FO_NONE:
						return GL_LINEAR;
				}
				break;
			case FO_POINT:
			case FO_NONE:
				switch(mMipFilter)
				{
					case FO_ANISOTROPIC:
					case FO_LINEAR:
						return GL_NEAREST_MIPMAP_LINEAR;
					case FO_POINT:
						return GL_NEAREST_MIPMAP_NEAREST;
					case FO_NONE:
						return GL_NEAREST;
				}
				break;
		}
		
		return 0;
	}
	
	void GLESRenderSystem::_setTextureUnitFiltering(size_t unit, FilterType ftype, FilterOptions fo)
	{
		if (!activateGLTextureUnit(unit))
			return;
			
		switch(ftype)
		{
			case FT_MIN:
				if (mTextureMipmapCount == 0)
				{
					mMinFilter = FO_NONE;
				}
				else
				{
					mMinFilter = fo;
				}
				
				glTexParameteri(GL_TEXTURE_2D,
								GL_TEXTURE_MIN_FILTER,
								getCombinedMinMipFilter());
				GL_CHECK_ERROR;
				break;
				
			case FT_MAG:
				switch(fo)
				{
					case FO_ANISOTROPIC:
					case FO_LINEAR:
						glTexParameteri(GL_TEXTURE_2D,
										GL_TEXTURE_MAG_FILTER,
										GL_LINEAR);
						GL_CHECK_ERROR;
						break;
					case FO_POINT:
					case FO_NONE:
						glTexParameteri(GL_TEXTURE_2D,
										GL_TEXTURE_MAG_FILTER,
										GL_NEAEST);
						GL_CHECK_ERROR;
						break;
				}
				break;
			case FT_MIP:
				if (mTextureMipmapCount == 0)
				{
					mMipFilter = FO_NONE;
				}
				else
				{
					mMipFilter = fo;
				}
				
				glTexParamteri(GL_TEXTURE_2D,
							   GL_TEXTURE_MIN_FILTER,
							   getCombinedMinMipFilter());
				GL_CHECK_ERROR;
				break;
		}
		
		activateGLTextureUnit(0);
	}
	
	GLfloat GLESRenderSystem::_getCurrentAnisotropy(size_t unit)
	{
		GLfloat curAniso = 0;
		glGetTexParameterfv(GL_TEXTURE_2D,
							GL_TEXTURE_MAX_ANISOTROPY_EXT, &curAniso);
		return curAniso ? curAniso : 1;
	}
	
	void GLESRenderSystem::_setTextureLayerAnisotropy(size_t unit, unsigned int maxAnisotropy)
	{
		if (!mCurrentCapabilities->hasCapability(RSC_ANISOTROPY))
			return;
			
		if (!activateGLTextureUnit(unit))	
			return;
			
		GLfloat largest_supported_anisotropy = 0;
		glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);
		if (maxAnisotropy > largest_supported_anisotropy)
			maxAnisotropy = largest_supported_anisotropy ?
				static_cast<uint>(largest_supported_anisotropy) : 1;
		if (_getCurrentAnisotropy(unit) != maxAnisotropy)
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);
			
		activateGLTextureUnit(0);
	}
	
	void GLESRenderSystem::setVertexDeclaration(VertexDeclaration* decl)
	{
	}
	
	void GLESRenderSystem::setVertexBufferBinding(VertexBufferBinding* binding)
	{
	}
	
	void GLESRenderSystem::_render(const RenderOperation& op)
	{
		GL_CHECK_ERROR;
		RenderSystem::_render(op);
		
		void* pBufferData = 0;
		bool multitexturing = (getCapabilities()->getNumTextureUnits() > 1);
		
		const VertexDeclaration::VertexElementList& decl = 
			op.vertexData->vertexDeclaration->getElements();
		
		VertexDeclaration::VertexElementList::const_iterator elem, elemEnd;
		
		elemEnd = decl.end();
		
		std::vector<GLuint> attribsBound;
		
		for (elem = decl.begin(); elem != elemEnd; ++elem)
		{
			if (!op.vertexData->vertexBufferBinding->isBufferBound(elem->getSource()))
				continue;
			
			HardwareVertexBufferSharedPtr vertexBuffer = 
				op.vertexData->vertexBufferBinding->getBuffer(elem->getSource());
			if (mCurrentCapabilities->hasCapability(RSC_VBO))
			{
				glBindBuffer(GL_ARRAY_BUFFER,
					static_cast<const GLESHardwareVertexBuffer*>(vertexBuffer.get())->getGLBufferId());
				GL_CHECK_ERROR;
				pBufferData = VBO_BUFFER_OFFSET(elem->getOffset());
			}
			else
			{
				pBufferData = static_cast<const GLESDefaultHardwareVertexBuffer*>(vertexBuffer.get())->getDataPtr(elem->getOffset());
			}
			
			if (op.vertexData->vertexStart)
			{
				pBufferData = static_cast<char*>(pBufferData) + op.vertexData->vertexStart * vertexBuffer->getVertexSize();
			}
			
			unsigned int i = 0;
			VertexElementSemantic sem = elem->getSemantic();
			{
				GL_CHECK_ERROR;
				switch(sem)
				{
					case VES_POSITION:
						glVertexPointer(VertexElement::getTypeCount(elem->getType()),
									    GLESHardwareBufferManager::getGLType(elem->getType()),
										static_cast<GLsizei>(vertexBuffer->getVertexSize()),
										pBufferData);
						GL_CHECK_ERROR;
						glEnableClientState(GL_VERTEX_ARRAY);
						GL_CHECK_ERROR;
						break;
					case VES_NORMAL:
						glNormalPointer(GLESHardwareBufferManager::getGLType(elem->getType()),
										static_cast<GLsizei>(vertexBuffer->getVertexSize()),
										pBufferData);
						GL_CHECK_ERROR;
						glEnableClientState(GL_NORMAL_ARRAY);
						GL_CHECK_ERROR;
						break;
					case VES_DIFFUSE:
						glColorPointer(4, GLESHardwareBufferManager::getGLType(elem->getType()),
									   static_cast<GLsizei>(vertexBuffer->getVertexSize()),
									   pBufferData);
						GL_CHECK_ERROR;
						glEnableClientState(GL_COLOR_ARRAY);
						GL_CHECK_ERROR;
						break;
					case VES_SPECULAR:
						break;
					case VES_TEXTURE_COORDINATES:
						{
							for (i = 0; i < mDisabledTexUnitsFrom; i++)
							{
								if (mTextureCoordIndex[i] == elem->getIndex() && i < mFixedFunctionTextureUnits)
								{
									GL_CHECK_ERROR;
									if (multitexturing)
										glClientActiveTexture(GL_TEXTURE0 + i);
									GL_CHECK_ERROR;
									glTexCoordPointer(VertexElement::getTypeCount(elem->getType()),
													  GLESHardwareBufferManager::getGLType(elem->getType()),
													  static_cast<GLsizei>(vertexBuffer->getVertexSize()),
													  pBufferData);
									GL_CHECK_ERROR;
									glEnableClientState(GL_TEXTURE_COORD_ARRAY);
									GL_CHECK_ERROR;
								}
							}
						}
						break;
					default:
						break;
				};
			}
		}
		
		if (multitexturing)
			glClientActiveTexture(GL_TEXTURE0);
		GL_CHECK_ERROR;
		
		GLint primType;
		switch(op.operationType)
		{
			case RenderOperation::OT_POINT_LIST:
				primType = GL_POINTS;
				break;
			case RenderOperation::OT_LINE_LIST:
				primType = GL_LINES;
				break;
			case RenderOperation::OT_LINE_STRIP:
				primType = GL_LINE_STRIP;
				break;
			default:
			case RenderOperation::OT_TRIANGLE_LIST:
				primType = GL_TRIANGLES;
				break;
			case RenderOperation::OT_TRIANGLE_STRIP:
				primType = GL_TRIANGLE_STRIP;
				break;
			case RenderOperation::OT_TRIANGLE_FAN:
				primType = GL_TRIANGLE_FAN;
				break;
		}
		
		if (op.useIndexes)
		{
			if (mCurrentCapabilities->hasCapability(RSC_VBO))
			{
				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
							  static_cast<GLESHardwareIndexBuffer*>(op.indexData->indexBuffer.get())->getGLBufferId());
							  
				GL_CHECK_ERROR;
				pBufferData = VBO_BUFFER_OFFSET(op.indexData->indexStart *
												op.indexData->indexBuffer->getIndexSize());
			}
			else
			{
				pBufferData = static_cast<GLESDefaultHardwareIndexBuffer*>(
								op.indexData->indexBuffer.get())->getDataPtr(
								op.indexData->indexStart * op.indexData->indexBuffer->getIndexSize());
			}
			
			GLenum indexType = (op.indexData->indexBuffer->getType() == HardwareIndexBuffer::IT_16BIT) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE;
			
			do
			{
				if (mDerivedDepthBias && mCurrentPassIterationNum > 0)
				{
					_setDepthBias(mDerivedDepthBiasBase + 
								  mDerivedDepthBiasMultiplier * mCurrentPassIterationNum,
								  mDerivedDepthBiasSlopScale);
				}
				GL_CHECK_ERROR;
				glDrawElements(primType, op.indexData->indexCount, indexType, pBufferData);
				GL_CHECK_ERROR;
			} while(updatePassIterationRenderState());
		}
		else
		{
			do
			{
				if (mDerivedDepthBias && mCurrentPassIterationNum > 0)
				{
					_setDepthBias(mDerivedDepthBiasBase + 
								  mDerivedDepthBiasMultiplier * mCurrentPassIterationNum,
								  mDerivedDepthBiasSlopScale);
				}
				glDrawArrays(primType, 0, op.vertexData->vertexCount);
				GL_CHECK_ERROR;
			} while(updatePassIterationRenderState());
		}
		
		glDisableClientState(GL_VERTEX_ARRAY);
		GL_CHECK_ERROR;
		
		if (multitexturing)
		{
			for (int i = 0; i < mFixedFunctionTextureUnits; i++)
			{
				glClientActiveTexture(GL_TEXTURE0 + i);
				glDisableClientState(GL_TEXTURE_COORD_ARRAY);
			}
			glClientActiveTexture(GL_TEXTURE0);
		}
		else
		{
			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		}
		GL_CHECK_ERROR;
		glDisableClientState(GL_NORMAL_ARRAY);
		GL_CHECK_ERROR;
		glDisableClientState(GL_COLOR_ARRAY);
		GL_CHECK_ERROR;
		
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::setScissorTest(bool enabled, size_t left,
										  size_t top, size_t right,
										  size_t bottom)
	{
		bool flipping = mActiveRenderTarget->requiresTextureFlipping();
		size_t targetHeight = mActiveRenderTarget->getHeight();
		GLsizei w, h, x, y;
		
		if (enabled)
		{
			glEnable(GL_SCISSOR_TEST);
			x = left;
			if (flipping)
				y = top;
			else
				y = targetHeight - bottom;
			w = right - left;
			h = bottom - top;
			glScissor(x, y, w, h);
		}
		else
		{
			glDisable(GL_SCISSOR_TEST);
			w = mActiveViewport->getActualWidth();
			h = mActiveViewport->getActualHeight();
			x = mActiveViewport->getActualLeft();
			if (flipping)
				y = mActiveViewport->getActualTop();
			else
				y = targetHeight - mActiveViewport->getActualTop() - h;
			
			glScissor(x, y, w, h);
		}
	}
	
	void GLESRenderSystem::clearFrameBuffer(unsigned int buffers,
											const ColourValue& colour,
											Real depth, unsigned short stencil)
	{
		bool colourMask = !mColourWrite[0] || !mColourWrite[1] 
						  !mColourWrite[2] || !mColourWrite[3];
		
		GLbitfield flags = 0;
		if (buffers & FBT_COLOUR)
		{
			flags |= GL_COLOR_BUFFER_BIT;
			if (colourMask)
			{
				glColorMask(true, true, true, true);
				GL_CHECK_ERROR;
			}
			glClearColor(colour.r, colour.g, colour.b, colour.a);
			GL_CHECK_ERROR;
		}
		if (buffers & FBT_DEPTH)
		{
			flags |= GL_DEPTH_BUFFER_BIT;
			if (!mDepthWrite)
			{
				glDepthMask(GL_TRUE);
				GL_CHECK_ERROR;
			}
			glClearDepthf(depth);
			GL_CHECK_ERROR;
		}
		if (buffers & FBT_STENCIL)
		{
			flags |= GL_STENCIL_BUFFER_BIT;
			glStencilMask(0xFFFFFFFF);
			GL_CHECK_ERROR;
			glClearStencil(stencil);
			GL_CHECK_ERROR;
		}
		
		GLboolean scissorTestEnabled = glIsEnabled(GL_SCISSOR_TEST);
		GL_CHECK_ERROR;
		if (!scissorTestEnabled)
		{
			glEnable(GL_SCISSOR_TEST);
			GL_CHECK_ERROR;
		}
		
		GLint viewport[4], scissor[4];
		glGetIntegerv(GL_VIEWPORT, viewport);
		GL_CHECK_ERROR;
		glGetIntegerv(GL_SCISSOR_BOX, scissor);
		GL_CHECK_ERROR;
		bool scissorBoxDifference = 
			viewport[0] != scissor[0] || viewport[1] != scissor[1] ||
			viewport[2] != scissor[2] || viewport[3] != scissor[3];
		if (scissorBoxDifference)
		{
			glScissor(viewport[0], viewport[1], viewport[2], viewport[3]);
			GL_CHECK_ERROR;
		}
		
		glClear(flags);
		GL_CHECK_ERROR;
		
		if (scissorBoxDifference)
		{
			glScissor(scissor[0], scissor[1], scissor[2], scissor[3]);
			GL_CHECK_ERROR;
		}
		
		if (!mDepthWrite && (buffers & FBT_DEPTH))
		{
			glDepthMask(GL_FALSE);
			GL_CHECK_ERROR;
		}
		
		if (colourMask && (buffers & FBT_COLOUR))
		{
			glColorMask(mColourWrite[0], mColourWrite[1], mColourWrite[2], mColourWrite[3]);
			GL_CHECK_ERROR;
		}
		
		if (buffers & FBT_STENCIL)
		{
			glStencilMask(mStencilMask);
			GL_CHECK_ERROR;
		}
	}
	
	HardwareOcclusionQuery* GLESRenderSystem::createHardwareOcclusionQuery(void)
	{
		return 0;
	}
	
	Real GLESRenderSystem::getHorizontalTexelOffset(void)
	{
		return 0.0;
	}
	
	Real GLESRenderSystem::getVerticalTexelOffset(void)
	{
		return 0.0;
	}
	
	Real GLESRenderSystem::getMinimumDepthInputValue(void)
	{
		return -1.0f;
	}
	
	Real GLESRenderSystem::getMaximumDepthInputValue(void)
	{
		return 1.0f;
	}
	
	void GLESRenderSystem::registerThread()
	{
	}
	
	void GLESRenderSystem::unregisterThread()
	{
	}
	
	void GLESRenderSystem::preExtraThreadsStarted()
	{
	}
	
	void GLESRenderSystem::postExtraThreadsStarted()
	{
	}
	
	void GLESRenderSystem::setClipPlanesImpl(const Ogre::PlaneList& clipPlanes)
	{
		int i = 0;
		int numClipPlanes;
		GLfloat clipPlane[4];
		
		glMatrixView(GL_MODELVIEW);
		GL_CHECK_ERROR;
		glPushMatrix();
		GL_CHECK_ERROR;
		GLfloat mat[16];
		makeGLMatrix(mat, mViewMatrix);
		glLoadMatrixf(mat);
		GL_CHECK_ERROR;
		
		numClipPlanes = clipPlanes.size();
		
		GLint maxClip;
		glGetIntegerv(GL_MAX_CLIP_PLANES, &maxClip);
		
		for (i = 0; i < numClipPlanes; ++i)
		{
			GLenum clipPlaneId = static_cast<GLenum>(GL_CLIP_PLANE0 + i);
			const Plane& plane = clipPlanes[i];
			
			if (i >= maxClip)
			{
				OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
							"Unable to set clip plane",
							"GLESRenderSystem::setClipPlanes");
			}
			
			clipPlane[0] = plane.normal.x;
			clipPlane[1] = plane.normal.y;
			clipPlane[2] = plane.normal.z;
			clipPlane[3] = plane.d;
			
			glClipPlanef(clipPlaneId, clipPlane);
			GL_CHECK_ERROR;
			glEnable(clipPlaneId);
			GL_CHECK_ERROR;
		}
		
		for (; i < maxClip; ++i)
		{
			glDisable(static_cast<GLenum>(GL_CLIP_PLANE0 + i));
			GL_CHECK_ERROR;
		}
		
		glPopMatrix();
		GL_CHECK_ERROR;
	}
	
	void GLESRenderSystem::_switchContext(GLESContext* context)
	{
		for (unsigned short i = 0; i < mCurrentLights; ++i)
		{
			setGLLight(i, NULL);
			mLights[i] = NULL;
		}
		mCurrentLights = 0;
		
		_disableTextureUnitsFrom(0);
		
		if (mCurrentContext)
			mCurrentContext->endCurrent();
		mCurrentContext = context;
		mCurrentContext->setCurrent();
		
		if (!mCurrentContext->getInitialized())
		{
			_oneTimeContextInitialization();
			mCurrentContext->setInitialized();
		}
		
		glDepthMask(mDepthWrite);
		glColorMask(mColourWrite[0], mColourWrite[1], mColourWrite[2], mColourWrite[3]);
		glStencilMask(mStencilMask);
	}
	
	void GLESRenderSystem::_unregisterContext(GLESContext* context)
	{
		if (mCurrentContext == context)
		{
			if (mCurrentContext != mMainContext)
			{
				_switchContext(mMainContext);
			}
			else
			{
				mCurrentContext->endCurrent();
				mCurrentContext = 0;
				mMainContext = 0;
			}
		}
	}
	
	void GLESRenderSystem::_oneTimeContextInitialization()
	{
		glDisable(GL_DITHER);
		GL_CHECK_ERROR;
		
		int fsaa_active = false;
		glGetIntegerv(GL_SAMPLE_BUFFERS, (GLint*)&fsaa_active);
		GL_CHECK_ERROR;
		if (fsaa_active)
		{
			glEnable(GL_MULTISAMPLE);
			GL_CHECK_ERROR;
			LogManager::getSingleton().logMessage("Using FSAA OpenGL ES.");
		}
	}
	
	void GLESRenderSystem::initialiseContext(RenderWindow* primary)
	{
		mMainContext = 0;
		primary->getCustomAttribute("GLCONTEXT", &mMainContext);
		mCurrentContext = mMainContext;
		
		if (mCurrentContext)
			mCurrentContext->setCurrent();
			
		mGLSupport->initialiseExtensions();
		
		LogManager::getSingleton().logMessage("**************************************");
		LogManager::getSingleton().logMessage("*** OpenGL ES 1.x Renderer Started ***");
		LogManager::getSingleton().logMessage("**************************************");
	}
	
	void GLESRenderSystem::_setRenderTarget(RenderTarget *target)
	{
		if (mActiveRenderTarget)
			mRTTManager->unbind(mActiveRenderTarget);
		
		mActiveRenderTarget = target;
		
		GLESContext* newContext = 0;
		target->getCustomAttribute("GLCONTEXT", &newContext);
		if (newContext && mCurrentContext != newContext)
		{
			_switchContext(newContext);
		}
		
		GLESDepthBuffer* depthBuffer = static_cast<GLESDepthBuffer*>(target->getDepthBuffer());
		
		if (target->getDepthBufferPool() != DepthBuffer::POOL_NO_DEPTH &&
			(!depthBuffer || depthBuffer->getGLContext() != mCurrentContext))
		{
			setDepthBufferFor(target);
		}
		
		mRTTManager->bind(target);
	}
	
	void GLESRenderSystem::makeGLMatrix(GLfloat gl_matrix[16], const Matrix4& m)
	{
		size_t x = 0;
		
		for (size_t i = 0; i < 4; i++)
		{
			for (size_t j = 0; j < 4; j++)
			{
				gl_matrix[x] = m[j][i];
				x++;
			}
		}
	}
	
	GLint GLESRenderSystem::convertCompareFunction(CompareFunction func) const
	{
		switch(func)
		{
			case CMPF_ALWAYS_FAIL:
				return GL_NEVER;
			case CMPF_ALWAYS_PASS:
				return GL_ALWAYS;
			case CMPF_LESS:
				return GL_LESS;
			case CMPF_LESS_EQUAL:
				return GL_LEQUAL;
			case CMPF_EQUAL:
				return GL_EQUAL;
			case CMPF_NOT_EQUAL:
				return GL_NOTEQUAL;
			case CMPF_GREATER_EQUAL:
				return GL_GEQUAL;
			case CMPF_GREATOR:
				return GL_GREATOR;
		};
		
		return GL_ALWAYS;
	}
	
	GLint GLESRenderSystem::convertStencilOp(StencilOperation op, bool invert) const
	{
		switch(op)
		{
			case SOP_KEEP:
				return GL_KEEP;
			case SOP_ZERO:
				return GL_ZERO;
			case SOP_REPLACE:
				return GL_REPLACE;
			case SOP_INCREMENT_WRAP:
			case SOP_INCREMENT:
				return invert ? GL_DECR : GL_INCR;
			case SOP_DECREMENT_WRAP:
			case SOP_DECREMENT:
				return invert ? GL_INCR : GL_DECR;
			case SOP_INVERT:
				return GL_INVERT;
		};
		
		return SOP_KEEP;
	}
	
	void GLESRenderSystem::setLights()
	{
		for (size_t i = 0; i < MAX_LIGHTS; ++i)
		{
			if (mLights[i] != NULL)
			{
				Light* lt = mLights[i];
				setGLLightPositionDirection(lt, GL_LIGHT0 + i);
			}
		}
	}
	
	void GLESRenderSystem::setGLLightPositionDirection(Light* lt, GLenum lightindex)
	{
		Vector4 vec;
		vec = lt->getAs4DVector();
		
#if OGRE_DOUBLE_PRECISION
		float tmp[4] = {vec.x, vec.y, vec.z, vec.w};
		glLightfv(lightindex, GL_POSITION, tmp);
#else
		glLightfv(lightindex, GL_POSITION, vec.ptr);
#endif
		if (lt->getType() == Light::LT_SPOTLIGHT)
		{
			vec = lt->getDerivedDirection();
			vec.w = 0.0;
#if OGRE_DOUBLE_PRECISION
		float tmp2[4] = {vec.x, vec.y, vec.z, vec.w};
		glLightfv(lightindex, GL_SPOT_DIRECTION, tmp2);
#else	
		glLightfv(lightindex, GL_SPOT_DIRECTION, vec.ptr());
#endif
		}
	}
	
	void GLESRenderSystem::setGLLight(size_t index, Light* lt)
	{
		GLenum gl_index = GL_LIGHT0 + index;
		
		if (!lt)
		{
			glDisable(gl_index);
			GL_CHECK_ERROR;
		}
		else
		{
			switch(lt->getType())
			{
				case Light::LT_SOPTLIGHT:
					glLightf(gl_index, GL_SPOT_CUTOFF, 0.5f * lt->getSpotlightOuterAngle().valueDegrees());
					GL_CHECK_ERROR;
					glLightf(gl_index, GL_SPOT_EXPONENT, lt->getSpotlightFalloff());
					GL_CHECK_ERROR;
					break;
				default:
					glLightf(gl_index, GL_SPOT_CUTOFF, 180.0);
					GL_CHECK_ERROR;
					break;
			}
			
			ColourValue col;
			col = lt->getDiffuseColour();
			
			GLfloat f4vals[4] = {col.r, col.g, col.b, col.a};
			glLightfv(gl_index, GL_DIFFUSE, f4vals);
			GL_CHECK_ERROR;
			
			col = lt->getSpecularColour();
			f4vals[0] = col.r;
			f4vals[1] = col.g;
			f4vals[2] = col.b;
			f4vals[3] = col.a;
			glLightfv(gl_index, GL_SPECULAR, f4vals);
			GL_CHECK_ERROR;
			
			f4vals[0] = 0;
			f4vals[1] = 0;
			f4vals[2] = 0;
			f4vals[3] = 0;
			glLightfv(gl_index, GL_AMBIENT, f4vals);
			GL_CHECK_ERROR;
			
			setGLLightPositionDirection(lt, gl_index);
			
			glLightf(gl_index, GL_CONSTANT_ATTENUATION, lt->getAttenuationConstant());
			GL_CHECK_ERROR;
			glLightf(gl_index, GL_LINEAR_ATTENUATION, lt->getAttenuationLinear());
			GL_CHECK_ERROR;
			glLightf(gl_index, GL_QUADRATIC_ATTENUATION, lt->getAttenuationQuadric());
			GL_CHECK_ERROR;
			
			glEnable(gl_index);
			GL_CHECK_ERROR;
		}
	}
	
	void GLESRenderSystem::bindGpuProgram(GpuProgram* prg)
	{
	}
	
	void GLESRenderSystem::unbindGpuProgram(GpuProgramType gptype)
	{
	}
	
	void GLESRenderSystem::bindGpuProgramParameters(GpuProgramType gptype, GpuProgramParametersSharedPtr params, uint16 mask)
	{
	}
	
	void GLESRenderSystem::bindGpuProgramPassIterationParameters(GpuProgramType gptype)
	{
	}
	
	unsigned int GLESRenderSystem::getDisplayMonitorCount() const
	{
		return 1;
	}
	
	bool GLESRenderSystem::activateGLTextureUnit(size_t unit)
	{
		if (mActiveTextureUnit != unit)
		{
			if (unit < getCapabilities()->getNumTextureUnits())
			{
				glActiveTexture(GL_TEXTURE0 + unit);
				GL_CHECK_ERROR;
				mActiveTextureUnit = unit;
				return true;
			}
			else if (!unit)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return true;
		}
	}
} 
